/**
 * Copyright (c) 2010-2011 Pierre LABATUT
 *
 * This file is part of multiple-track-player project.
 *
 * multiple-track-player is free software: you can redistribute it and/or modify it
 * under the terms of its license which may be found in the accompanying
 * LICENSE file or <http://code.google.com/p/multiple-track-player/>.
 */
package multitrack.preload;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.sound.sampled.AudioFormat;

/**
 * Gather multiple audio buffers into a single mixed PCM stream.
 * 
 * @author Pierre LABATUT
 */
public class MixAudioBuffer implements AudioBuffer {

    final AudioFormat format;
    final List<Track> tracks;
    float floatChunk[] = new float[0];
    
    public MixAudioBuffer(final List<HeapAudioBuffer> buffers) {
        if (buffers.isEmpty()) {
            throw new IllegalArgumentException("At least one file must be loaded");
        }
        int channels = buffers.get(0).format().getChannels();
        
        final ArrayList<Track> ttracks = new ArrayList<Track>();
        for (HeapAudioBuffer buffer : buffers) {
            
            if (buffer.format().getChannels() != channels) {
                throw new IllegalArgumentException("Channels count is different between tracks");
            }
            ttracks.add(new Track(buffer));
        }
        this.format = buffers.get(0).format();
        this.tracks = Collections.unmodifiableList(ttracks);
    }
    
    @Override
    public int read(int fromSample, int sampleCount, short[] target) throws InterruptedException {        
        if (sampleCount > floatChunk.length) {
            floatChunk = new float[sampleCount];
        }

        //FIXME Multithreading issue
        boolean atLeastOneSolo = false;
        for (final Track track : tracks) {
            atLeastOneSolo |= track.getSolo();
        }

        // Sum every input
        Arrays.fill(floatChunk, 0);
        int maxSampleRead = 0;
        for (final Track track : tracks) {

            /**
             * Evaluate the volume. Continue without reading the buffers would not be a good idea, because
             * when every channel is mute, no data would be read, and the stream would be considered as over.
             */
            float volume = track.getVolume();
            if (atLeastOneSolo) {
                if (track.getSolo() == false) {
                    volume = 0;
                }
            } else if (track.getMute()) {
                volume = 0;
            }
            
            final int readed = track.buffer.read(fromSample, sampleCount, target);
            maxSampleRead = Math.max(maxSampleRead, readed);
            for (int i = 0; i < readed; i++) {
                floatChunk[i] += target[i] * volume;
            }
        }


        // Get the average
        for (int i = 0; i < maxSampleRead; i++) {
            target[i] = (short) floatChunk[i];
        }        
        return maxSampleRead;        
    }
    
    @Override
    public AudioFormat format() {
        return format;
    }
    
    @Override
    public int samples() {
        int samples = 0;
        for (Track t : tracks) {
            int bframes = t.buffer.samples();
            if (bframes > samples) {
                samples = bframes;
            }
        }
        return samples;
    }

   
}
